import domian.ConfigMap;
import domian.Deployment;
import domian.VolumeMount;
import io.kubernetes.client.custom.*;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.Configuration;
import io.kubernetes.client.openapi.apis.AppsV1Api;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.models.*;
import io.kubernetes.client.util.ClientBuilder;
import io.kubernetes.client.util.Config;
import io.kubernetes.client.util.KubeConfig;
import io.kubernetes.client.util.generic.GenericKubernetesApi;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 *     k8s工具类
 * </p>
 * @author wind
 * @date    2024/12/11 18:01
 * @version v1.0
 */
public class K8sHelper {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private final String config;

    private final String url;

    private final String token;

    private ApiClient apiClient;

    private K8sHelper(String config, String url, String token){
        this.config = config;
        this.url = url;
        this.token = token;
        this.initClient();
    }

    /**
     * 用于从指定的Kubernetes API服务器URL
     * @param url
     * @return
     */
    public static K8sHelper fromUrl(String url){
        return new K8sHelper(null, url, null);
    }

    /**
     * token访问
     * @param url
     * @param token
     * @return
     */
    public static K8sHelper fromToken(String url, String token){
        return new K8sHelper(null, url, token);
    }

    /**
     * $HOME/.kube/config(系统配置文件)
     * @param config
     * @return
     */
    public static K8sHelper fromConfig(String config){
        return new K8sHelper(config, null, null);
    }

    /**
     * 初始化客户端
     */
    private void initClient(){
        ApiClient apiClient;
        if(url != null && token != null){
            apiClient = Config.fromToken(url, token, false);
        }else if(url != null){
            apiClient = Config.fromUrl(url,  false);
        }else{
            if(this.config == null){
                throw new RuntimeException("初始化k8s客户端异常, k8s配置文件不存在");
            }
            String classpath = "classpath:";
            if(this.config.startsWith(classpath)){
                String configPath = this.config.replaceAll(classpath, "");
                InputStream in = K8sHelper.class.getResourceAsStream(configPath);
                if(in == null){
                    throw new RuntimeException("初始化k8s客户端异常, k8s配置文件不存在");
                }
                try (BufferedReader br = new BufferedReader(new InputStreamReader(in))){
                    apiClient =  ClientBuilder.kubeconfig(KubeConfig.loadKubeConfig(br)).build();
                } catch (Exception e) {
                    logger.error("初始化k8s客户端异常", e);
                    throw new RuntimeException("初始化k8s客户端异常",e);
                }
            }else{
                try (BufferedReader br = new BufferedReader(new FileReader(this.config))){
                    apiClient =  ClientBuilder.kubeconfig(KubeConfig.loadKubeConfig(br)).build();
                } catch (Exception e) {
                    logger.error("初始化k8s客户端异常", e);
                    throw new RuntimeException("初始化k8s客户端异常", e);
                }
            }
        }
        if(apiClient == null){
            throw new RuntimeException("初始化k8s客户端失败");
        }
        this.apiClient = apiClient;
//        this.apiClient.setDebugging(true);
        Configuration.setDefaultApiClient(this.apiClient);
    }

    /**
     * deployment部署
     * @param deployment
     * @return
     */
    public V1Deployment createDeploy(Deployment deployment) {
        String namespace = deployment.getNamespace();
        String name = deployment.getName();
        String cpu = deployment.getCpu();
        String memory = deployment.getMemory();
        int replicas = deployment.getReplicas();
        String engineNO = deployment.getEngineNO();
        //构建service的yaml对象
        V1Deployment pvc = new V1Deployment();
        pvc.setApiVersion("apps/v1");
        pvc.kind("Deployment");
        // metadata
        V1ObjectMeta metadata = new V1ObjectMeta();
        Map<String, String> labels = new HashMap<>();
        labels.put("app", name);
        metadata.setLabels(labels);
        metadata.setName(name);
        metadata.setNamespace(namespace);
        pvc.setMetadata(metadata);
        // spec
        V1DeploymentSpec spec = new V1DeploymentSpec();
        spec.setReplicas(replicas);

        V1LabelSelector v1LabelSelector = new V1LabelSelector();
        v1LabelSelector.setMatchLabels(labels);
        spec.setSelector(v1LabelSelector);

        V1PodTemplateSpec template = new V1PodTemplateSpec();
        V1PodSpec v1PodSpec = new V1PodSpec();
        v1PodSpec.setNodeSelector(deployment.getNodeSelector());
        template.setMetadata(metadata);


        List<V1Container> containers = new ArrayList<>();
        V1Container container = new V1Container();
        container.setName(name);
        // image  镜像地址名称
        container.setImage(deployment.getImage());
        V1ResourceRequirements v1ResourceRequirements = new V1ResourceRequirements();
        Map<String, Quantity> limits = new HashMap<>();
        limits.put("cpu", new Quantity(cpu));
        limits.put("memory", new Quantity(memory + "Gi"));
        Map<String, Quantity> requests = new HashMap<>();
        requests.put("cpu", new Quantity(cpu));
        requests.put("memory", new Quantity(memory + "Gi"));
        v1ResourceRequirements.setLimits(limits);
        v1ResourceRequirements.setRequests(requests);
        container.setResources(v1ResourceRequirements);
        if(StringUtils.isNotEmpty(engineNO)){
            List<String> args = new ArrayList<>();
            args.add("--load.engineNO=" + engineNO);
            container.setArgs(args);
        }
        List<V1ContainerPort> ports = new ArrayList<>();
        V1ContainerPort v1ContainerPort = new V1ContainerPort();
        v1ContainerPort.setContainerPort(deployment.getPort());
        ports.add(v1ContainerPort);
        container.setEnv(deployment.getEnvList());
        container.setPorts(ports);
        containers.add(container);
        v1PodSpec.setContainers(containers);

        List<VolumeMount> volumeMountList = deployment.getVolumeMountList();
        List<V1VolumeMount> volumeMounts = new ArrayList<>();
        List<V1Volume> volumes = new ArrayList<>();
        if(!CollectionUtils.isEmpty(volumeMountList)){
            for(VolumeMount vm : volumeMountList){
                V1VolumeMount v1VolumeMount = new V1VolumeMount();
                v1VolumeMount.setMountPath(vm.getMountPath());
                v1VolumeMount.setName(vm.getName());
                String subPath = vm.getSubPath();
                if(StringUtils.isNotEmpty(subPath)){
                    v1VolumeMount.setSubPath(subPath);
                }
                volumeMounts.add(v1VolumeMount);

                V1Volume v1Volume = new V1Volume();
                v1Volume.setName(vm.getName());
                String hostPath = vm.getHostPath();
                ConfigMap configMap = vm.getConfigMap();
                String claimName = vm.getClaimName();
                if(StringUtils.isNotEmpty(hostPath)){
                    V1HostPathVolumeSource source = new V1HostPathVolumeSource();
                    source.setPath(vm.getHostPath());
                    source.setType("DirectoryOrCreate");
                    v1Volume.setHostPath(source);
                }else if(configMap != null){
                    V1ConfigMapVolumeSource source = new V1ConfigMapVolumeSource();
                    source.setName(configMap.getName());
                    source.setItems(configMap.getItems());
                    v1Volume.setConfigMap(source);
                }else if(StringUtils.isNotEmpty(claimName)){
                    V1PersistentVolumeClaimVolumeSource source = new V1PersistentVolumeClaimVolumeSource();
                    source.setClaimName(claimName);
                    v1Volume.setPersistentVolumeClaim(source);
                }
                volumes.add(v1Volume);
            }
        }
        container.setVolumeMounts(volumeMounts);
        v1PodSpec.setVolumes(volumes);

        List<V1LocalObjectReference> imagePullSecrets = new ArrayList<>();
        V1LocalObjectReference ref = new V1LocalObjectReference();
        ref.setName(deployment.getImagePullSecrets());
        imagePullSecrets.add(ref);
        v1PodSpec.setImagePullSecrets(imagePullSecrets);
        template.setSpec(v1PodSpec);
        spec.setTemplate(template);
        pvc.setSpec(spec);
        return execApps((api, c) -> {
            return api.createNamespacedDeployment(namespace, pvc,null,null,null);
        }, V1Deployment.class);
    }

    /**
     * 删除deploy
     * @param namespace
     * @param name
     * @return
     */
    public boolean deleteDeploy(String namespace, String name){
        execApps((api, c) -> {
            return api.deleteNamespacedDeployment(name, namespace, null,null,null, null, null, null);
        }, V1Status.class);
        return true;
    }

    /**
     * 创建k8s service
     *
     * @param namespace   命名空间
     * @param name       POD名称
     * @param image      镜像名称
     * @param  cpu      cpu大小
     * @param memory     内存
     * @return 创建成功的POD对象
     */
    public V1Pod createPod(String namespace, String name, String image, Integer port, String cpu, String memory) {
        //构建service的yaml对象
        V1Pod pvc = new V1Pod();
        V1PodSpec v1PodSpec = new V1PodSpec();
        List<V1Container> containers = new ArrayList<>();
        V1Container container = new V1Container();
        container.setName(name);
        V1ResourceRequirements v1ResourceRequirements = new V1ResourceRequirements();
        Map<String, Quantity> limits = new HashMap<>();
        limits.put("cpu", new Quantity(cpu));
        Map<String, Quantity> requests = new HashMap<>();
        requests.put("memory", new Quantity(memory));
        v1ResourceRequirements.setLimits(limits);
        v1ResourceRequirements.setRequests(requests);
        container.setResources(v1ResourceRequirements);
        container.setImage(image);
        List<V1ContainerPort> ports = new ArrayList<>();
        V1ContainerPort v1ContainerPort = new V1ContainerPort();
        v1ContainerPort.setContainerPort(port);
        ports.add(v1ContainerPort);
        container.setPorts(ports);
        containers.add(container);
        v1PodSpec.setContainers(containers);
        pvc.setSpec(v1PodSpec);
        V1ObjectMeta v1ObjectMeta = new V1ObjectMeta();
        v1ObjectMeta.setName(name);
        v1ObjectMeta.setNamespace(namespace);
        Map<String, String> labels = new HashMap<>();
        labels.put("app", name);
        v1ObjectMeta.setLabels(labels);
        pvc.setMetadata(v1ObjectMeta);
        return execCore((api, c) -> {
            return api.createNamespacedPod(namespace, pvc, null, null, null);
        }, V1Pod.class);
    }

    /**
     * 根据名称创建pod
     * @param name
     * @param nameSpace
     * @return
     */
    public V1Pod getPod(String name, String nameSpace) {
        return execCore((api, c) -> {
            return api.readNamespacedPod(name, nameSpace, "true", null, null);
        }, V1Pod.class);
    }

    /**
     * 根据名称创建pod
     * @param name
     * @param namespace
     * @return
     */
    public V1PodList listPod(String namespace, String name) {
        return execCore((api, c) -> {
            String labelSelector = null;
            if(StringUtils.isNotEmpty(name)){
                labelSelector = "app=" + name;
            }
            return api.listNamespacedPod(namespace, null, null,null, null, labelSelector, null
                    ,null, null, null, null);
        }, V1PodList.class);
    }


    /**
     * 删除pod
     * @param name
     * @param namespace
     * @return
     */
    public boolean deletePod(String namespace, String name) {
        return execCore((api, c) -> {
            api.deleteNamespacedPod(name, namespace,null,null,null,null,null,
                    null);
            return true;
        }, Boolean.class);
    }

    /**
     * 创建svc
     * @param namespace
     * @param name
     * @param appName
     * @param type
     * @param port
     * @param targetPort
     */
    public void createSvc(String namespace, String name, String appName, String type, Integer port, Integer targetPort){
        V1Service service = new V1Service()
                .apiVersion("v1")
                .kind("Service")
                .metadata(new V1ObjectMeta().name(name))
                .spec(new V1ServiceSpec().selector(new HashMap<String, String>(){{
                    put("app", appName);
                }}).type(type).ports(new ArrayList<V1ServicePort>(){{
                    add(new V1ServicePort().port(port).targetPort(new IntOrString(targetPort)));
                }}))
                ;

        execCore((api, c) -> {
            return api.createNamespacedService(namespace, service, null, null, null);
        }, V1Service.class);
    }

    /**
     * 删除svc
     * @param namespace
     * @param name
     */
    public void deleteSvc(String namespace, String name){
        execCore((api, c) -> {
            return api.deleteNamespacedService(name, namespace, null, null, null, null, null, null);
        }, V1Status.class);
    }


    /**
     * 获取所有命名空间
     * @return
     */
    public List<String> listNs(){
        return execCore((api, c) -> {
            List<String> nsList = new ArrayList<>();
            V1NamespaceList list = api.listNamespace(null, null, null, null, null,
                    null, null, null, null, null);
            for(V1Namespace space : list.getItems()){
                nsList.add(space.getMetadata().getName());
            }
            return nsList;
        }, List.class);
    }


    /**
     * 获取node的资源情况
     * @return
     */
    public NodeMetricsList getNodeMetric(){
        GenericKubernetesApi<NodeMetrics, NodeMetricsList> api =  new GenericKubernetesApi<>(
                NodeMetrics.class,
                NodeMetricsList.class,
                "metrics.k8s.io",
                "v1",
                "nodes",
                this.apiClient);
        return api.list().getObject();
    }

    /**
     * 获取node的资源情况
     * @return
     */
    public PodMetricsList getPodMetric(String namespace){
        GenericKubernetesApi<PodMetrics, PodMetricsList> api =  new GenericKubernetesApi<>(
                PodMetrics.class,
                PodMetricsList.class,
                "metrics.k8s.io",
                "v1beta1",
                "pods",
                this.apiClient);
        return api.list(namespace).getObject();
    }

    /**
     * 执行
     * @param callback
     */

    public <T> T execCore(CoreV1Callback<T> callback, Class<T> c){
        try {
            CoreV1Api api = new CoreV1Api(this.apiClient);
            notNull(callback, "k8s CoreV1Api回调操作不能为空");
            return callback.action(api, c);
        } catch (Exception e) {
            logger.error("K8sClientUtil.exec exception, msg:{}", e.getMessage());
            throw new RuntimeException(e);
        }
    }

    /**
     * 执行
     * @param callback
     */

    public <T> T execApps(AppsV1Callback<T> callback, Class<T> c){
        try {
            AppsV1Api api = new AppsV1Api(this.apiClient);
            notNull(callback, "k8s AppsV1Api回调操作不能为空");
            return callback.action(api, c);
        } catch (Exception e) {
            logger.error("K8sClientUtil.exec exception, msg:{}", e.getMessage());
            throw new RuntimeException(e);
        }
    }


    /**
     * 不能为空
     * @param obj
     */
    private void notNull(Object obj, String msg){
        if(obj == null){
            throw new IllegalArgumentException(msg);
        }
    }

    @FunctionalInterface
    interface AppsV1Callback<T> {

        /**
         * k8s执行操作
         * @param api
         * @return
         * @throws ApiException
         */
        T action(AppsV1Api api, Class<T> c) throws ApiException;
    }

    @FunctionalInterface
    interface CoreV1Callback<T> {

        /**
         * k8s执行操作
         * @param api
         * @return
         * @throws ApiException
         */
        T action(CoreV1Api api, Class<T> c) throws ApiException, ApiException;
    }
}
